package interpreter;

import parser.ASTNode;
import parser.NodeType;
import lexer.Token;
import lexer.TokenType;
import semantic.SymbolTable;
import java.util.List;
import java.util.HashMap;
import java.util.Map;

public class Interpreter {
    private SymbolTable symbolTable;
    private Map<String, Object> variables;

    // 模拟的股票数据（在实际应用中应该从数据库或API获取）
    private Map<String, double[]> stockData;

    public Interpreter() {
        this.symbolTable = new SymbolTable();
        this.variables = new HashMap<>();
        initializeStockData();
        initializeBuiltInFunctions();
    }

    public Interpreter(SymbolTable symbolTable) {
        this.symbolTable = symbolTable;
        this.variables = new HashMap<>();
        initializeStockData();
        initializeBuiltInFunctions();
    }

    private void initializeStockData() {
        // 模拟股票数据（收盘价）
        stockData = new HashMap<>();
        double[] closePrices = {10.5, 11.2, 10.8, 12.1, 11.9, 13.2, 12.8, 14.1, 13.5, 15.2};
        stockData.put("CLOSE", closePrices);

        double[] openPrices = {10.2, 11.0, 10.9, 11.8, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5};
        stockData.put("OPEN", openPrices);

        double[] highPrices = {11.0, 11.8, 11.5, 12.5, 12.8, 13.5, 13.8, 14.5, 14.2, 15.5};
        stockData.put("HIGH", highPrices);

        double[] lowPrices = {10.0, 10.8, 10.5, 11.5, 11.2, 12.2, 12.5, 13.2, 13.0, 14.0};
        stockData.put("LOW", lowPrices);

        double[] volumes = {1000.0, 1200.0, 800.0, 1500.0, 1300.0, 1800.0, 1600.0, 2000.0, 1700.0, 2200.0};
        stockData.put("VOL", volumes);
    }

    private void initializeBuiltInFunctions() {
        // 这里可以注册内置函数，但实际计算在visitFunctionCall中处理
    }

    public Object interpret(ASTNode node) {
        return visit(node);
    }

    private Object visit(ASTNode node) {
        switch (node.getType()) {
            case PROGRAM:
                return visitProgram(node);
            case STATEMENT_LIST:
                return visitStatementList(node);
            case ASSIGNMENT_STATEMENT:
                return visitAssignment(node);
            case EXPRESSION:
                return visitExpression(node);
            case BINARY_OPERATION:
                return visitBinaryOperation(node);
            case UNARY_OPERATION:
                return visitUnaryOperation(node);
            case VARIABLE:
                return visitVariable(node);
            case CONSTANT:
                return visitConstant(node);
            case FUNCTION_CALL:
                return visitFunctionCall(node);
            case IF_STATEMENT:
                return visitIfStatement(node);
            case FOR_LOOP:
                return visitForLoop(node);
            case WHILE_LOOP:
                return visitWhileLoop(node);
            default:
                throw new RuntimeException("Unsupported node type: " + node.getType());
        }
    }

    private Object visitProgram(ASTNode node) {
        Object result = null;
        for (ASTNode child : node.getChildren()) {
            result = visit(child);
        }
        return result;
    }

    private Object visitStatementList(ASTNode node) {
        Object result = null;
        for (ASTNode child : node.getChildren()) {
            result = visit(child);
        }
        return result;
    }

    private Object visitAssignment(ASTNode node) {
        String varName = node.getToken().getValue();
        Object value = visit(node.getChildren().get(0));
        variables.put(varName, value);
        return value;
    }

    private Object visitExpression(ASTNode node) {
        if (!node.getChildren().isEmpty()) {
            return visit(node.getChildren().get(0));
        }
        return null;
    }

    private Object visitBinaryOperation(ASTNode node) {
        Object left = visit(node.getChildren().get(0));
        Object right = visit(node.getChildren().get(1));
        TokenType op = node.getToken().getType();

        double leftNum = toNumber(left);
        double rightNum = toNumber(right);

        switch (op) {
            case PLUS: return leftNum + rightNum;
            case MINUS: return leftNum - rightNum;
            case MULTIPLY: return leftNum * rightNum;
            case DIVIDE:
                if (rightNum == 0) throw new RuntimeException("Division by zero");
                return leftNum / rightNum;
            case MOD: return leftNum % rightNum;
            case EQ: return leftNum == rightNum ? 1.0 : 0.0;
            case NE: return leftNum != rightNum ? 1.0 : 0.0;
            case GT: return leftNum > rightNum ? 1.0 : 0.0;
            case GE: return leftNum >= rightNum ? 1.0 : 0.0;
            case LT: return leftNum < rightNum ? 1.0 : 0.0;
            case LE: return leftNum <= rightNum ? 1.0 : 0.0;
            case AND: return (toBoolean(left) && toBoolean(right)) ? 1.0 : 0.0;
            case OR: return (toBoolean(left) || toBoolean(right)) ? 1.0 : 0.0;
            default:
                throw new RuntimeException("Unsupported operator: " + op);
        }
    }

    private Object visitUnaryOperation(ASTNode node) {
        Object operand = visit(node.getChildren().get(0));
        TokenType op = node.getToken().getType();
        double num = toNumber(operand);

        switch (op) {
            case PLUS: return +num;
            case MINUS: return -num;
            case NOT: return toBoolean(operand) ? 0.0 : 1.0;
            default:
                throw new RuntimeException("Unsupported unary operator: " + op);
        }
    }

    private Object visitVariable(ASTNode node) {
        String varName = node.getToken().getValue();

        // 首先检查是否是内置变量
        if (stockData.containsKey(varName)) {
            // 返回整个数组（对于技术指标计算）
            return stockData.get(varName);
        }

        // 然后检查用户定义的变量
        if (variables.containsKey(varName)) {
            return variables.get(varName);
        }

        throw new RuntimeException("Undefined variable: " + varName);
    }

    private Object visitConstant(ASTNode node) {
        Token token = node.getToken();
        if (token.getType() == TokenType.NUMBER) {
            return Double.parseDouble(token.getValue());
        } else if (token.getType() == TokenType.STRING) {
            return token.getValue();
        }
        throw new RuntimeException("Unsupported constant type: " + token.getType());
    }

    private Object visitFunctionCall(ASTNode node) {
        String funcName = node.getToken().getValue();
        List<ASTNode> args = node.getChildren();

        switch (funcName.toUpperCase()) {
            case "MA":
                return calculateMA(args);
            case "CROSS":
                return calculateCross(args);
            case "IF":
                return calculateIf(args);
            case "MAX":
                return calculateMax(args);
            case "MIN":
                return calculateMin(args);
            case "SUM":
                return calculateSum(args);
            default:
                throw new RuntimeException("Unsupported function: " + funcName);
        }
    }

    // 移动平均线计算
    private Object calculateMA(List<ASTNode> args) {
        if (args.size() < 2) {
            throw new RuntimeException("MA function requires 2 parameters: data and period");
        }

        Object dataObj = visit(args.get(0));
        Object periodObj = visit(args.get(1));

        if (!(dataObj instanceof double[]) || !(periodObj instanceof Double)) {
            throw new RuntimeException("MA function parameters type error");
        }

        double[] data = (double[]) dataObj;
        int period = ((Double) periodObj).intValue();

        if (period <= 0 || period > data.length) {
            throw new RuntimeException("Invalid period for MA function");
        }

        double[] result = new double[data.length];
        for (int i = 0; i < data.length; i++) {
            if (i < period - 1) {
                result[i] = Double.NaN; // 前期数据不足
            } else {
                double sum = 0;
                for (int j = 0; j < period; j++) {
                    sum += data[i - j];
                }
                result[i] = sum / period;
            }
        }

        return result;
    }

    // 交叉判断
    private Object calculateCross(List<ASTNode> args) {
        if (args.size() != 2) {
            throw new RuntimeException("CROSS function requires 2 parameters");
        }

        Object data1Obj = visit(args.get(0));
        Object data2Obj = visit(args.get(1));

        if (!(data1Obj instanceof double[]) || !(data2Obj instanceof double[])) {
            throw new RuntimeException("CROSS function parameters must be arrays");
        }

        double[] data1 = (double[]) data1Obj;
        double[] data2 = (double[]) data2Obj;

        if (data1.length != data2.length) {
            throw new RuntimeException("CROSS function arrays must have same length");
        }

        // 检查最后两个周期是否有交叉
        if (data1.length < 2) {
            return 0.0;
        }

        int lastIndex = data1.length - 1;
        boolean crossUp = (data1[lastIndex - 1] <= data2[lastIndex - 1]) &&
                (data1[lastIndex] > data2[lastIndex]);

        return crossUp ? 1.0 : 0.0;
    }

    // 条件判断
    private Object calculateIf(List<ASTNode> args) {
        if (args.size() != 3) {
            throw new RuntimeException("IF function requires 3 parameters: condition, true_value, false_value");
        }

        Object condition = visit(args.get(0));
        Object trueValue = visit(args.get(1));
        Object falseValue = visit(args.get(2));

        return toBoolean(condition) ? trueValue : falseValue;
    }

    // 最大值
    private Object calculateMax(List<ASTNode> args) {
        if (args.size() < 2) {
            throw new RuntimeException("MAX function requires at least 2 parameters");
        }

        double max = Double.NEGATIVE_INFINITY;
        for (ASTNode arg : args) {
            Object value = visit(arg);
            double num = toNumber(value);
            if (num > max) {
                max = num;
            }
        }
        return max;
    }

    // 最小值
    private Object calculateMin(List<ASTNode> args) {
        if (args.size() < 2) {
            throw new RuntimeException("MIN function requires at least 2 parameters");
        }

        double min = Double.POSITIVE_INFINITY;
        for (ASTNode arg : args) {
            Object value = visit(arg);
            double num = toNumber(value);
            if (num < min) {
                min = num;
            }
        }
        return min;
    }

    // 求和
    private Object calculateSum(List<ASTNode> args) {
        if (args.size() < 2) {
            throw new RuntimeException("SUM function requires at least 2 parameters");
        }

        double sum = 0;
        for (ASTNode arg : args) {
            Object value = visit(arg);
            double num = toNumber(value);
            sum += num;
        }
        return sum;
    }

    private Object visitIfStatement(ASTNode node) {
        List<ASTNode> children = node.getChildren();
        Object condition = visit(children.get(0));

        if (toBoolean(condition)) {
            return visit(children.get(1));
        } else if (children.size() > 2) {
            return visit(children.get(2));
        }
        return null;
    }

    private Object visitForLoop(ASTNode node) {
        // 简化实现，实际应用中需要更复杂的循环处理
        Object result = null;
        for (ASTNode child : node.getChildren()) {
            result = visit(child);
        }
        return result;
    }

    private Object visitWhileLoop(ASTNode node) {
        // 简化实现
        Object result = null;
        for (ASTNode child : node.getChildren()) {
            result = visit(child);
        }
        return result;
    }

    // 工具方法
    private double toNumber(Object obj) {
        if (obj instanceof Double) {
            return (Double) obj;
        } else if (obj instanceof Integer) {
            return ((Integer) obj).doubleValue();
        } else if (obj instanceof String) {
            try {
                return Double.parseDouble((String) obj);
            } catch (NumberFormatException e) {
                throw new RuntimeException("Cannot convert to number: " + obj);
            }
        } else if (obj instanceof double[]) {
            double[] array = (double[]) obj;
            return array.length > 0 ? array[array.length - 1] : 0.0;
        }
        throw new RuntimeException("Unsupported type for number conversion: " + obj.getClass());
    }

    private boolean toBoolean(Object obj) {
        if (obj instanceof Double) {
            return ((Double) obj) != 0.0;
        } else if (obj instanceof Integer) {
            return ((Integer) obj) != 0;
        } else if (obj instanceof Boolean) {
            return (Boolean) obj;
        } else if (obj instanceof String) {
            return !((String) obj).isEmpty();
        }
        return false;
    }

    // 获取计算结果
    public Map<String, Object> getVariables() {
        return new HashMap<>(variables);
    }

    public void displayResults() {
        System.out.println("=== 公式计算结果 ===");
        for (Map.Entry<String, Object> entry : variables.entrySet()) {
            Object value = entry.getValue();
            if (value instanceof double[]) {
                double[] array = (double[]) value;
                System.out.printf("%s: [", entry.getKey());
                for (int i = 0; i < Math.min(array.length, 5); i++) {
                    System.out.printf("%.2f", array[i]);
                    if (i < Math.min(array.length, 5) - 1) {
                        System.out.print(", ");
                    }
                }
                if (array.length > 5) {
                    System.out.print(", ...");
                }
                System.out.println("]");
            } else {
                System.out.printf("%s: %s%n", entry.getKey(), value);
            }
        }
    }
}